﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.IO;
using System.Xml;
using ConfigGen.Core.TemplateLoader;
using ConfigGen.Utilities;
using Moq;
using NUnit.Framework;

namespace ConfigGen.Core.Tests.TemplateLoader
{
    [TestFixture]
    public class FileTemplateLoaderTests
    {
        [Test]
        public void Ctor_NullArg()
        {
            Assert.Throws<ArgumentNullException>(() => new FileTemplateLoader(null));
        }

        [Test]
        public void LoadTemplate_BadArgs()
        {
            var itemUnderTest = new FileTemplateLoader(new TemplatePreferencesLoader());
            var exception1 = Assert.Throws<ArgumentNullException>(() => itemUnderTest.LoadTemplate(null), "args null: incorrect exception");
            Assert.AreEqual("args", exception1.ParamName, "args null: incorrect parameter");

            var exception2 = Assert.Throws<ArgumentException>(() => itemUnderTest.LoadTemplate(new string[0]), "args empty: incorrect exception");
            Assert.AreEqual("args", exception2.ParamName, "args empty: incorrect parameter");

            var exception3 = Assert.Throws<ArgumentNullException>(() => itemUnderTest.LoadTemplate(new string[] { null }), "args[0] null: incorrect exception");
            Assert.AreEqual("args", exception3.ParamName, "args[0] null: incorrect parameter");

            var exception4 = Assert.Throws<ArgumentException>(() => itemUnderTest.LoadTemplate(new [] { "" }), "args[0] empty: incorrect exception");
            Assert.AreEqual("args", exception4.ParamName, "args[0] empty: incorrect parameter");

            Assert.Throws<FileNotFoundException>(() => itemUnderTest.LoadTemplate(new [] { "NoFile" }), "file not found: incorrect exception");
        }

        [Test]
        public void LoadsTemplateCorrectly()
        {
            const string xml = @"<?xml version=""1.0""?>
<configuration xmlns:cg=""http://roblevine.co.uk/Namespaces/ConfigGen/1/0/"">
    <appSettings>
        <add key=""MySetting"" value=""MyValue""/>
    </appSettings>
</configuration>";

            using (var testDirectory = new DisposableDirectory())
            {
                var templatePath = Path.Combine(testDirectory.FullName, "template.xml");
                using (var fileWriter = new StreamWriter(templatePath))
                {
                    fileWriter.Write(xml);
                }

                var itemUnderTest = new FileTemplateLoader(new TemplatePreferencesLoader());
                itemUnderTest.LoadTemplate(new [] {templatePath});

                var stream = itemUnderTest.GetTemplateStream();
                Assert.IsNotNull(stream, "template stream: should not be null");
                var xmlDocument = new XmlDocument();
                xmlDocument.Load(templatePath);
                Assert.IsNotNull(xmlDocument.DocumentElement, "Root element should be present");
                Assert.IsNotNull(xmlDocument.SelectSingleNode("/configuration/appSettings/add[@key='MySetting']"), "Setting not found in loaded template");
            } 
        }

        [Test]
        public void GetTemplateCanBeCalledMultipleTimes()
        {
            const string xml = @"<?xml version=""1.0""?>
<configuration xmlns:cg=""http://roblevine.co.uk/Namespaces/ConfigGen/1/0/"">
    <appSettings>
        <add key=""MySetting"" value=""MyValue""/>
    </appSettings>
</configuration>";

            using (var testDirectory = new DisposableDirectory())
            {
                var templatePath = Path.Combine(testDirectory.FullName, "template.xml");
                using (var fileWriter = new StreamWriter(templatePath))
                {
                    fileWriter.Write(xml);
                }

                var itemUnderTest = new FileTemplateLoader(new TemplatePreferencesLoader());
                itemUnderTest.LoadTemplate(new[] { templatePath });

                var stream = itemUnderTest.GetTemplateStream();
                Assert.IsNotNull(stream, "First get of stream: template stream: should not be null");
                var xmlDocument = new XmlDocument();
                xmlDocument.Load(templatePath);
                Assert.IsNotNull(xmlDocument.DocumentElement, "First get of stream: Root element should be present");
                Assert.IsNotNull(xmlDocument.SelectSingleNode("/configuration/appSettings/add[@key='MySetting']"), "First get of stream: Setting not found in loaded template");

                stream = itemUnderTest.GetTemplateStream();
                Assert.IsNotNull(stream, "Second get of stream: template stream: should not be null");
                xmlDocument = new XmlDocument();
                xmlDocument.Load(templatePath);
                Assert.IsNotNull(xmlDocument.DocumentElement, "Second get of stream: Root element should be present");
                Assert.IsNotNull(xmlDocument.SelectSingleNode("/configuration/appSettings/add[@key='MySetting']"), "Second get of stream: Setting not found in loaded template");
            }
        }

        [Test]
        public void TemplatePreferencesExtractorCalledCorrectly()
        {
            const string xml = @"<?xml version=""1.0""?>
<configuration xmlns:cg=""http://roblevine.co.uk/Namespaces/ConfigGen/1/0/"">
    <appSettings>
        <add key=""MySetting"" value=""MyValue""/>
    </appSettings>
</configuration>";

            long sourceStreamLength = -1;
            long destinationStreamLength = -1;
            var templatePreferences = new TemplatePreferences();
            var mockTemplatePreferencesExtractor = new Mock<ITemplatePreferencesLoader>(MockBehavior.Loose);
            mockTemplatePreferencesExtractor
                .Setup(e => e.LoadPreferences(It.IsAny<Stream>(), It.IsAny<Stream>()))
                .Callback((Stream src, Stream dest) =>
                              {
                                  if (src != null) sourceStreamLength = src.Length;
                                  if (dest != null) destinationStreamLength = dest.Length;
                              })
                .Returns(templatePreferences);
                   

            using (var testDirectory = new DisposableDirectory())
            {
                var templatePath = Path.Combine(testDirectory.FullName, "template.xml");

                

                using (var fileWriter = new StreamWriter(templatePath))
                {
                    fileWriter.Write(xml);
                }

                var itemUnderTest = new FileTemplateLoader(mockTemplatePreferencesExtractor.Object);
                itemUnderTest.LoadTemplate(new[] { templatePath });

                Assert.AreNotEqual(0, sourceStreamLength, "source stream arg: incorrect length");
                Assert.AreEqual(0, destinationStreamLength, "destination stream arg: incorrect length");
                Assert.AreSame(templatePreferences, itemUnderTest.TemplatePreferences, "TemplatePreferences: incorrect");
            }
        }
    }
}
